using Microsoft.AspNetCore.Http;
using System.IO.Compression;
using System.Threading.Tasks;

namespace Terra.Core.Middleware.Performance
{
    /// <summary>
    /// 响应压缩中间件
    /// </summary>
    public class ResponseCompressionMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly string[] _compressibleMimeTypes = new[]
        {
            "text/plain",
            "text/html",
            "text/css",
            "text/javascript",
            "application/javascript",
            "application/json",
            "application/xml"
        };

        public ResponseCompressionMiddleware(RequestDelegate next)
        {
            _next = next;
        }

        public async Task InvokeAsync(HttpContext context)
        {
            var acceptEncoding = context.Request.Headers["Accept-Encoding"].ToString().ToLower();
            var supportsGzip = acceptEncoding.Contains("gzip");
            var supportsDeflate = acceptEncoding.Contains("deflate");

            if (supportsGzip || supportsDeflate)
            {
                var originalBody = context.Response.Body;
                using var compressionStream = new MemoryStream();
                context.Response.Body = compressionStream;

                await _next(context);

                if (ShouldCompress(context))
                {
                    context.Response.Headers.Append("Content-Encoding", supportsGzip ? "gzip" : "deflate");
                    using var compressed = new MemoryStream();

                    if (supportsGzip)
                    {
                        using var gzip = new GZipStream(compressed, CompressionMode.Compress, true);
                        compressionStream.Seek(0, SeekOrigin.Begin);
                        await compressionStream.CopyToAsync(gzip);
                    }
                    else
                    {
                        using var deflate = new DeflateStream(compressed, CompressionMode.Compress, true);
                        compressionStream.Seek(0, SeekOrigin.Begin);
                        await compressionStream.CopyToAsync(deflate);
                    }

                    compressed.Seek(0, SeekOrigin.Begin);
                    await compressed.CopyToAsync(originalBody);
                }
                else
                {
                    compressionStream.Seek(0, SeekOrigin.Begin);
                    await compressionStream.CopyToAsync(originalBody);
                }

                context.Response.Body = originalBody;
            }
            else
            {
                await _next(context);
            }
        }

        private bool ShouldCompress(HttpContext context)
        {
            var contentType = context.Response.ContentType?.ToLower() ?? "";
            return _compressibleMimeTypes.Any(type => contentType.Contains(type));
        }
    }
}